package khet.gui;

import gggameserver.games.Position;
import gggameserver.games.khet.KhetBoard;
import gggameserver.games.khet.KhetMove;
import gggameserver.games.khet.KhetPiece;
import gggameserver.games.khet.KhetPiece.Type;
import gggameserver.games.khet.KhetShotResult;

import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;

import javax.imageio.ImageIO;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JPanel;

public class KhetGuiBoard extends JPanel implements MouseListener,
		KhetMoveListener {

	public static final Color BOARD_COLOR = new Color(222, 192, 152);

	private static final long serialVersionUID = 1L;

	public static final Color LASER_COLOR_BACK = new Color(100, 255, 100);
	public static final Color LASER_COLOR = Color.green;

	private static final int SCORE_IMAGE_SIZE = 24;
	private static final int SCORE_IMAGE_SPACE = 2;

	public KhetBoard khetBoard = null;
	private KhetMoveListener moveListener = null;

	private boolean imagesBuffered = false;
	// load the images
	private BufferedImage[] anubisRed;
	private BufferedImage[] anubisSilver;
	private BufferedImage[] pharaohRed;
	private BufferedImage[] pharaohSilver;
	private BufferedImage[] pyramidRed;
	private BufferedImage[] pyramidSilver;
	private BufferedImage[] scarabRed;
	private BufferedImage[] scarabSilver;
	private BufferedImage[] sphinxRed;
	private BufferedImage[] sphinxSilver;
	private BufferedImage empty;
	private BufferedImage emptyAnkh;
	private BufferedImage emptyHorus;
	private BufferedImage ccw;
	private BufferedImage cw;
	private byte[] lightSaber;
	
	private boolean soundOn = true;

	private Map<Image, Queue<Map.Entry<Integer, Image>>> scaledImageCache = new HashMap<Image, Queue<Map.Entry<Integer, Image>>>();

	private Map<Position, Integer> possibleMoves = null;
	private KhetPiece lastSelectedPiece = null;

	public KhetGuiBoard() {
		this.addMouseListener(this);
		this.moveListener = this;
	}

	/**
	 * When in player-picks-move mode, then make move for him
	 */
	public synchronized void proposeNextMove(KhetMove move) {
		sendMove(move);
	}

	/**
	 * Use GUI board to get next move from player
	 */
	public synchronized void getNextMove(KhetMoveListener listener) {
		this.moveListener = listener; // TODO handle null's
	}
	
	private void initImages() throws IOException, UnsupportedAudioFileException {
		anubisRed = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/anubis_red_0.png")),
				ImageIO.read(getClass().getResource("/anubis_red_1.png")),
				ImageIO.read(getClass().getResource("/anubis_red_2.png")),
				ImageIO.read(getClass().getResource("/anubis_red_3.png")) };
		anubisSilver = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/anubis_silver_0.png")),
				ImageIO.read(getClass().getResource("/anubis_silver_1.png")),
				ImageIO.read(getClass().getResource("/anubis_silver_2.png")),
				ImageIO.read(getClass().getResource("/anubis_silver_3.png")) };
		pharaohRed = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/pharaoh_red_0.png")),
				ImageIO.read(getClass().getResource("/pharaoh_red_1.png")),
				ImageIO.read(getClass().getResource("/pharaoh_red_2.png")),
				ImageIO.read(getClass().getResource("/pharaoh_red_3.png")) };
		pharaohSilver = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/pharaoh_silver_0.png")),
				ImageIO.read(getClass().getResource("/pharaoh_silver_1.png")),
				ImageIO.read(getClass().getResource("/pharaoh_silver_2.png")),
				ImageIO.read(getClass().getResource("/pharaoh_silver_3.png")) };
		pyramidRed = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/pyramid_red_0.png")),
				ImageIO.read(getClass().getResource("/pyramid_red_1.png")),
				ImageIO.read(getClass().getResource("/pyramid_red_2.png")),
				ImageIO.read(getClass().getResource("/pyramid_red_3.png")) };
		pyramidSilver = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/pyramid_silver_0.png")),
				ImageIO.read(getClass().getResource("/pyramid_silver_1.png")),
				ImageIO.read(getClass().getResource("/pyramid_silver_2.png")),
				ImageIO.read(getClass().getResource("/pyramid_silver_3.png")) };
		scarabRed = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/scarab_red_0.png")),
				ImageIO.read(getClass().getResource("/scarab_red_1.png")),
				ImageIO.read(getClass().getResource("/scarab_red_2.png")),
				ImageIO.read(getClass().getResource("/scarab_red_3.png")) };
		scarabSilver = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/scarab_silver_0.png")),
				ImageIO.read(getClass().getResource("/scarab_silver_1.png")),
				ImageIO.read(getClass().getResource("/scarab_silver_2.png")),
				ImageIO.read(getClass().getResource("/scarab_silver_3.png")) };
		sphinxRed = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/sphinx_red_0.png")),
				ImageIO.read(getClass().getResource("/sphinx_red_1.png")),
				ImageIO.read(getClass().getResource("/sphinx_red_2.png")),
				ImageIO.read(getClass().getResource("/sphinx_red_3.png")) };
		sphinxSilver = new BufferedImage[] {
				ImageIO.read(getClass().getResource("/sphinx_silver_0.png")),
				ImageIO.read(getClass().getResource("/sphinx_silver_1.png")),
				ImageIO.read(getClass().getResource("/sphinx_silver_2.png")),
				ImageIO.read(getClass().getResource("/sphinx_silver_3.png")) };
		empty = ImageIO.read(getClass().getResource("/empty.png"));
		emptyAnkh = ImageIO.read(getClass().getResource("/empty_ankh.png"));
		emptyHorus = ImageIO.read(getClass().getResource("/empty_horus.png"));
		ccw = ImageIO.read(getClass().getResource("/counterclockwise.png"));
		cw = ImageIO.read(getClass().getResource("/clockwise.png"));
		
		InputStream in = getClass().getResourceAsStream("/lightsaber.wav");
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		int next = in.read();
		while (next > -1) {
		    bos.write(next);
		    next = in.read();
		}
		bos.flush();
		lightSaber = bos.toByteArray();
		
		imagesBuffered = true;
	}

	public void setKhetBoard(KhetBoard khetBoard) {
		this.khetBoard = khetBoard;
		repaint();
	}

	public class Metrix {
		public Graphics2D g2;
		public int x0, y0;
		// grid size
		public int cellSize;
		// space between cells
		public int squareD;
		// drawable square size
		public int squareSize;

		public Metrix(Graphics2D g2) {
			this.g2 = g2;

			int squareW = (getWidth() - 8 * (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE))
					/ (KhetBoard.BOARD_WIDTH + 1);
			int squareH = getHeight() / (KhetBoard.BOARD_HEIGHT + 1);
			this.cellSize = Math.min(squareW, squareH);
			this.squareD = 2;
			this.squareSize = Math.max(this.cellSize - 2 * this.squareD, 0);

			this.x0 = ((getWidth() - 8 * (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE)) - this.cellSize
					* KhetBoard.BOARD_WIDTH) / 2;
			this.y0 = (getHeight() - this.cellSize * KhetBoard.BOARD_HEIGHT) / 2;
		}
	}

	@Override
	public synchronized void paint(Graphics g) {
		super.paint(g);
		Graphics2D g2 = (Graphics2D) g;
		g2.setColor(BOARD_COLOR);
		g2.fillRect(0, 0, getWidth(), getHeight());

		drawBoard(new Metrix(g2), khetBoard);
	}

	private void drawBoard(Metrix m, KhetBoard board) {
		if (null == board)
			return;

		synchronized (board) {
			AffineTransform tx = m.g2.getTransform();
			m.g2.translate(m.x0, m.y0);

			drawGrid(m, board);
			if (null != this.moveListener)
				drawPossibleMoves(m, board);
			drawPieces(m, board);
			drawLaser(m, board);
			if (null != this.moveListener)
				drawPossibleRotation(m, board);
			drawScoreboard(m, board);

			m.g2.setTransform(tx);
		}
	}

	private Image getScaledImgInstance(Image img, int size, int dummyHints) {
		if (null == img)
			return null;
		if (size < 1)
			size = 1;

		// find/create queue
		Queue<Map.Entry<Integer, Image>> queue = scaledImageCache.get(img);
		if (null == queue) {
			queue = new LinkedList<Map.Entry<Integer, Image>>();
			scaledImageCache.put(img, queue);
		}

		// look from cache for proper entry, and remove that entry
		Iterator<Entry<Integer, Image>> it = queue.iterator();
		Entry<Integer, Image> scaledImgEntry = null;
		while (it.hasNext()) {
			Entry<Integer, Image> entry = it.next();
			if (size == entry.getKey().intValue()) {
				scaledImgEntry = entry;
				it.remove(); // we put that into back
			}
		}

		// was not in cache... create new instance
		if (null == scaledImgEntry) {
			Image scaledImg = img.getScaledInstance(size, size,
					Image.SCALE_SMOOTH);
			scaledImgEntry = new AbstractMap.SimpleEntry(size, scaledImg);
		}

		// add to the back
		queue.add(scaledImgEntry);

		// trim cache
		while (queue.size() > 2)
			queue.poll(); // removes first element

		return scaledImgEntry.getValue();
	}

	private void drawScoreboard(Metrix m, KhetBoard board) {
		KhetScore score = getBoardScore(board);
		m.g2.setColor(Color.BLACK);
		m.g2.drawLine(KhetBoard.BOARD_WIDTH * m.cellSize + 4 * SCORE_IMAGE_SIZE
				+ 4 * SCORE_IMAGE_SPACE + 10, 0, KhetBoard.BOARD_WIDTH
				* m.cellSize + 4 * SCORE_IMAGE_SIZE + 4 * SCORE_IMAGE_SPACE
				+ 10, 6 * SCORE_IMAGE_SIZE + 6 * SCORE_IMAGE_SPACE);
		m.g2.drawLine(KhetBoard.BOARD_WIDTH * m.cellSize + 10, 6
				* SCORE_IMAGE_SIZE + 6 * SCORE_IMAGE_SPACE,
				KhetBoard.BOARD_WIDTH * m.cellSize + 8 * SCORE_IMAGE_SIZE + 8
						* SCORE_IMAGE_SPACE + 10, 6 * SCORE_IMAGE_SIZE + 6
						* SCORE_IMAGE_SPACE);
		// pharaon
		if (score.pharaohSilver != 0) {
			m.g2.drawImage(
					getScaledImgInstance(pharaohSilver[0], 24,
							Image.SCALE_SMOOTH), KhetBoard.BOARD_WIDTH
							* m.cellSize + 3 * SCORE_IMAGE_SIZE + 3
							* SCORE_IMAGE_SPACE + 10, 0, null);
		}
		if (score.pharaohRed != 0) {
			m.g2.drawImage(
					getScaledImgInstance(pharaohRed[0], 24, Image.SCALE_SMOOTH),
					KhetBoard.BOARD_WIDTH * m.cellSize + 4 * SCORE_IMAGE_SIZE
							+ 5 * SCORE_IMAGE_SPACE + 11, 0, null);
		}

		// sphinx
		if (score.sphinxSilver != 0) {
			m.g2.drawImage(
					getScaledImgInstance(sphinxSilver[0], 24,
							Image.SCALE_SMOOTH), KhetBoard.BOARD_WIDTH
							* m.cellSize + 3 * SCORE_IMAGE_SIZE + 3
							* SCORE_IMAGE_SPACE + 10, SCORE_IMAGE_SIZE
							+ SCORE_IMAGE_SPACE, null);
		}
		if (score.sphinxRed != 0) {
			m.g2.drawImage(
					getScaledImgInstance(sphinxRed[0], 24, Image.SCALE_SMOOTH),
					KhetBoard.BOARD_WIDTH * m.cellSize + 4 * SCORE_IMAGE_SIZE
							+ 5 * SCORE_IMAGE_SPACE + 11, SCORE_IMAGE_SIZE
							+ SCORE_IMAGE_SPACE, null);
		}

		// anubis
		for (int i = 0; i < score.anubisSilver; i++) {
			m.g2.drawImage(
					getScaledImgInstance(anubisSilver[0], 24,
							Image.SCALE_SMOOTH), KhetBoard.BOARD_WIDTH
							* m.cellSize + 3 * SCORE_IMAGE_SIZE + 3
							* SCORE_IMAGE_SPACE + 10 - i
							* (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE), 2
							* SCORE_IMAGE_SIZE + 2 * SCORE_IMAGE_SPACE, null);
		}

		for (int i = 0; i < score.anubisRed; i++) {
			m.g2.drawImage(
					getScaledImgInstance(anubisRed[0], 24, Image.SCALE_SMOOTH),
					KhetBoard.BOARD_WIDTH * m.cellSize + 4 * SCORE_IMAGE_SIZE
							+ 5 * SCORE_IMAGE_SPACE + 11 + i
							* (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE), 2
							* SCORE_IMAGE_SIZE + 2 * SCORE_IMAGE_SPACE, null);
		}

		// scarab
		for (int i = 0; i < score.scarabSilver; i++) {
			m.g2.drawImage(
					getScaledImgInstance(scarabSilver[0], 24,
							Image.SCALE_SMOOTH), KhetBoard.BOARD_WIDTH
							* m.cellSize + 3 * SCORE_IMAGE_SIZE + 3
							* SCORE_IMAGE_SPACE + 10 - i
							* (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE), 3
							* SCORE_IMAGE_SIZE + 3 * SCORE_IMAGE_SPACE, null);
		}
		for (int i = 0; i < score.scarabRed; i++) {
			m.g2.drawImage(
					getScaledImgInstance(scarabRed[0], 24, Image.SCALE_SMOOTH),
					KhetBoard.BOARD_WIDTH * m.cellSize + 4 * SCORE_IMAGE_SIZE
							+ 5 * SCORE_IMAGE_SPACE + 11 + i
							* (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE), 3
							* SCORE_IMAGE_SIZE + 3 * SCORE_IMAGE_SPACE, null);
		}
		// pyramid
		for (int i = 0; i < score.pyramidSilver; i++) {
			if (i < 3) {
				m.g2.drawImage(
						getScaledImgInstance(pyramidSilver[0], 24,
								Image.SCALE_SMOOTH), KhetBoard.BOARD_WIDTH
								* m.cellSize + 3 * SCORE_IMAGE_SIZE + 3
								* SCORE_IMAGE_SPACE + 10 - i
								* (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE), 4
								* SCORE_IMAGE_SIZE + 4 * SCORE_IMAGE_SPACE,
						null);
			} else {
				m.g2.drawImage(
						getScaledImgInstance(pyramidSilver[0], 24,
								Image.SCALE_SMOOTH), KhetBoard.BOARD_WIDTH
								* m.cellSize + 3 * SCORE_IMAGE_SIZE + 3
								* SCORE_IMAGE_SPACE + 10 - (i - 3)
								* (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE), 5
								* SCORE_IMAGE_SIZE + 5 * SCORE_IMAGE_SPACE,
						null);

			}
		}
		for (int i = 0; i < score.pyramidRed; i++) {
			if (i < 3) {
				m.g2.drawImage(
						getScaledImgInstance(pyramidRed[0], 24,
								Image.SCALE_SMOOTH), KhetBoard.BOARD_WIDTH
								* m.cellSize + 4 * SCORE_IMAGE_SIZE + 5
								* SCORE_IMAGE_SPACE + 11 + i
								* (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE), 4
								* SCORE_IMAGE_SIZE + 4 * SCORE_IMAGE_SPACE,
						null);
			} else {
				m.g2.drawImage(
						getScaledImgInstance(pyramidRed[0], 24,
								Image.SCALE_SMOOTH), KhetBoard.BOARD_WIDTH
								* m.cellSize + 4 * SCORE_IMAGE_SIZE + 5
								* SCORE_IMAGE_SPACE + 11 + (i - 3)
								* (SCORE_IMAGE_SIZE + SCORE_IMAGE_SPACE), 5
								* SCORE_IMAGE_SIZE + 5 * SCORE_IMAGE_SPACE,
						null);

			}
		}

		m.g2.drawChars("Turn: ".toCharArray(), 0, 6, KhetBoard.BOARD_WIDTH
				* m.cellSize + 10, 7 * SCORE_IMAGE_SIZE + 7 * SCORE_IMAGE_SPACE);
		if (board.getActivePlayerNumber() == khetBoard.PLAYER_SILVER) {
			m.g2.setColor(new Color(117, 115, 116));
		} else {
			m.g2.setColor(new Color(154, 38, 38));
		}
		m.g2.fillRect(KhetBoard.BOARD_WIDTH * m.cellSize + 50,
				7 * SCORE_IMAGE_SIZE + 2, 130, 15);

	}

	private void drawLaser(Metrix m, KhetBoard board) {
		KhetShotResult shot = board.getLastShot();
		if (null == shot)
			return;

		
		ArrayList<Position> trajectory = shot.trajectory;
		for (int i = 1; i < trajectory.size(); ++i) {
			Position p0 = trajectory.get(i - 1);
			Position p1 = trajectory.get(i);
			
			m.g2.setColor(LASER_COLOR_BACK);
			m.g2.setComposite(AlphaComposite.getInstance(
					AlphaComposite.SRC_ATOP, 0.3F));
			m.g2.setStroke(new BasicStroke(5F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND ));
			m.g2.drawLine(m.cellSize * p0.getX() + m.cellSize / 2, m.cellSize
					* p0.getY() + m.cellSize / 2, m.cellSize * p1.getX()
					+ m.cellSize / 2, m.cellSize * p1.getY() + m.cellSize / 2);
			
			m.g2.setColor(LASER_COLOR);
			m.g2.setComposite(AlphaComposite.getInstance(
					AlphaComposite.SRC_ATOP, 0.6F));
			m.g2.setStroke(new BasicStroke(2.5F));
			m.g2.drawLine(m.cellSize * p0.getX() + m.cellSize / 2, m.cellSize
					* p0.getY() + m.cellSize / 2, m.cellSize * p1.getX()
					+ m.cellSize / 2, m.cellSize * p1.getY() + m.cellSize / 2);
			
			m.g2.setComposite(AlphaComposite.getInstance(
					AlphaComposite.SRC_ATOP, 1F));
			m.g2.setStroke(new BasicStroke(1F));
			m.g2.drawLine(m.cellSize * p0.getX() + m.cellSize / 2, m.cellSize
					* p0.getY() + m.cellSize / 2, m.cellSize * p1.getX()
					+ m.cellSize / 2, m.cellSize * p1.getY() + m.cellSize / 2);
			
		}

		if (shot.isPieceDestroyed()) {
			Position p0 = trajectory.get(trajectory.size() - 1);
			int x = m.cellSize * p0.getX() + m.cellSize / 2;
			int y = m.cellSize * p0.getY() + m.cellSize / 2;
			m.g2.drawLine(x - m.cellSize / 4, y - m.cellSize / 4, x
					+ m.cellSize / 4, y + m.cellSize / 4);
			m.g2.drawLine(x - m.cellSize / 4, y + m.cellSize / 4, x
					+ m.cellSize / 4, y - m.cellSize / 4);
		}
	}

	private void drawPieces(Metrix m, KhetBoard board) {
		if (!imagesBuffered) {
			try {
				initImages();
			} catch (Exception e) {
				System.err.println("Error loading images");
			}
		}
		for (int i = 0; i < KhetBoard.BOARD_WIDTH; ++i)
			for (int j = 0; j < KhetBoard.BOARD_HEIGHT; ++j) {
				KhetPiece piece = board.board[i][j];
				Image image = empty;
				if (piece != null) {
					switch (piece.getType()) {
					case PYRAMID:
						if (piece.getPlayerNumber() == 1) {
							image = pyramidSilver[piece.getDirection()];
						} else {
							image = pyramidRed[piece.getDirection()];
						}
						break;
					case ANUBIS:
						if (piece.getPlayerNumber() == 1) {
							image = anubisSilver[piece.getDirection()];
						} else {
							image = anubisRed[piece.getDirection()];
						}
						break;
					case SPHINX:
						if (piece.getPlayerNumber() == 1) {
							image = sphinxSilver[piece.getDirection()];
						} else {
							image = sphinxRed[piece.getDirection()];
						}
						break;
					case SCARAB:
						if (piece.getPlayerNumber() == 1) {
							image = scarabSilver[piece.getDirection()];
						} else {
							image = scarabRed[piece.getDirection()];
						}
						break;

					case PHARAON:
						if (piece.getPlayerNumber() == 1) {
							image = pharaohSilver[piece.getDirection()];
						} else {
							image = pharaohRed[piece.getDirection()];
						}
						break;
					}
				} else {
					if (i == 0 || (i == 8 && j == 0) || (i == 8 && j == 7)) {
						image = emptyHorus;
					} else if (i == 9 || (i == 1 && j == 0)
							|| (i == 1 && j == 7)) {
						image = emptyAnkh;
					}
				}

				m.g2.drawImage(
						getScaledImgInstance(image, m.squareSize - 1,
								Image.SCALE_SMOOTH), i * m.cellSize + m.squareD
								+ 1, j * m.cellSize + m.squareD + 1, null);

			}
	}

	private void drawGrid(Metrix m, KhetBoard board) {
		for (int i = 0; i < KhetBoard.BOARD_WIDTH; ++i)
			for (int j = 0; j < KhetBoard.BOARD_HEIGHT; ++j) {
				m.g2.setColor(Color.BLACK);
				m.g2.drawRect(i * m.cellSize, j * m.cellSize, m.cellSize,
						m.cellSize);
				if (board.special[i][j] != KhetBoard.PLAYER_EMPTY) {
					m.g2.setColor(getPlayerColor(board.special[i][j]));
					m.g2.fillRect(i * m.cellSize + 1, j * m.cellSize + 1,
							m.cellSize - 1, m.cellSize - 1);
				}
			}
	}

	private void drawPossibleMoves(Metrix m, KhetBoard board) {
		if (possibleMoves != null) {
			for (Position p : possibleMoves.keySet()) {
				m.g2.setColor(getPlayerColor(0));
				m.g2.fillRect(p.getX() * m.cellSize + 1, p.getY() * m.cellSize
						+ 1, m.cellSize - 1, m.cellSize - 1);
			}
		}
	}

	private void drawPossibleRotation(Metrix m, KhetBoard board) {
		if (lastSelectedPiece != null) {
			Composite oldComposite = m.g2.getComposite();

			m.g2.setComposite(AlphaComposite.getInstance(
					AlphaComposite.SRC_ATOP, 0.3F));
			if (khetBoard.isLeagal(new KhetMove(lastSelectedPiece
					.getPlayerNumber(), lastSelectedPiece.getX(),
					lastSelectedPiece.getY(), KhetMove.ACTION_TURN_CLOCKWISE))) {
				m.g2.setColor(Color.MAGENTA);
				m.g2.fillRect(lastSelectedPiece.getX() * m.cellSize + 1,
						lastSelectedPiece.getY() * m.cellSize + 1,
						m.cellSize - 1, (m.cellSize - 1) / 2);

				m.g2.setComposite(AlphaComposite.getInstance(
						AlphaComposite.SRC_ATOP, 1F));
				m.g2.drawImage(cw.getScaledInstance(m.squareSize / 2,
						m.squareSize / 3, Image.SCALE_SMOOTH),
						lastSelectedPiece.getX() * m.cellSize + m.cellSize
								- m.squareSize / 2, lastSelectedPiece.getY()
								* m.cellSize + m.squareD + 1, null);
			}

			if (khetBoard.isLeagal(new KhetMove(lastSelectedPiece
					.getPlayerNumber(), lastSelectedPiece.getX(),
					lastSelectedPiece.getY(),
					KhetMove.ACTION_TURN_COUNTERCLOCKWISE))) {
				m.g2.setColor(Color.GREEN);
				m.g2.setComposite(AlphaComposite.getInstance(
						AlphaComposite.SRC_ATOP, 0.3F));
				m.g2.fillRect(lastSelectedPiece.getX() * m.cellSize + 1,
						lastSelectedPiece.getY() * m.cellSize + 1
								+ (m.cellSize - 1) / 2, m.cellSize - 1,
						(m.cellSize - 1) / 2);

				m.g2.setComposite(AlphaComposite.getInstance(
						AlphaComposite.SRC_ATOP, 1F));
				m.g2.drawImage(ccw.getScaledInstance(m.squareSize / 2,
						m.squareSize / 3, Image.SCALE_SMOOTH),
						lastSelectedPiece.getX() * m.cellSize + m.cellSize
								- m.squareSize / 2, lastSelectedPiece.getY()
								* m.cellSize + m.squareD + m.cellSize
								- m.squareSize / 3 - 3, null);
			}

			m.g2.setComposite(oldComposite);
		}
	}

	private Color getPlayerColor(int player) {
		if (KhetBoard.PLAYER_SILVER == player)
			return new Color(179, 179, 179);
		else if (KhetBoard.PLAYER_RED == player)
			return new Color(211, 87, 87);
		else
			return Color.ORANGE;
	}

	@Override
	public synchronized void mouseClicked(MouseEvent e) {
		if (null == this.moveListener)
			return;

		Metrix m = new Metrix(null);
		int x = -1;
		int y = -1;
		if ((e.getX() - m.x0) >= 0
				&& (e.getX() - m.x0) / m.cellSize < KhetBoard.BOARD_WIDTH
				&& (e.getY() - m.y0) >= 0
				&& (e.getY() - m.y0) / m.cellSize < KhetBoard.BOARD_HEIGHT) {
			x = (e.getX() - m.x0) / m.cellSize;
			y = (e.getY() - m.y0) / m.cellSize;

			// calculate, whether click was made upper or lower part
			int rotate = ((e.getY() - m.y0) - y * m.cellSize) < (m.cellSize / 2) ? KhetMove.ACTION_TURN_CLOCKWISE
					: KhetMove.ACTION_TURN_COUNTERCLOCKWISE;
			KhetPiece piece = khetBoard.board[x][y];
			if (piece != null
					&& lastSelectedPiece == null
					&& piece.getPlayerNumber() == khetBoard
							.getActivePlayerNumber()) {
				// select piece for moving
				lastSelectedPiece = piece;
				getAllLegalMoves(x, y);
			} else if (lastSelectedPiece != null
					&& lastSelectedPiece.getX() == x
					&& lastSelectedPiece.getY() == y
					&& khetBoard.isLeagal(new KhetMove(lastSelectedPiece
							.getPlayerNumber(), lastSelectedPiece.getX(),
							lastSelectedPiece.getY(), rotate))) {
				KhetMove newMove = new KhetMove(
						lastSelectedPiece.getPlayerNumber(),
						lastSelectedPiece.getX(), lastSelectedPiece.getY(),
						rotate);
				sendMove(newMove);
				lastSelectedPiece = null;
				possibleMoves = null;
				playAudio();
			} else if (lastSelectedPiece != null && possibleMoves != null
					&& possibleMoves.get(new Position(x, y)) != null) { // piece
				KhetMove newMove = new KhetMove(
						lastSelectedPiece.getPlayerNumber(),
						lastSelectedPiece.getX(), lastSelectedPiece.getY(),
						possibleMoves.get(new Position(x, y)).intValue());
				sendMove(newMove);
				lastSelectedPiece = null;
				possibleMoves = null;
				playAudio();
			} else {// player clicked to random place
				lastSelectedPiece = null;
				possibleMoves = null;
			}
		}
		repaint();
	}

	private void sendMove(KhetMove newMove) {
		if (null == this.moveListener)
			return;

		KhetMoveListener ml = this.moveListener;
		if (this != this.moveListener)
			this.moveListener = null;

		ml.makeMove(khetBoard, newMove);
	}

	@Override
	public void makeMove(KhetBoard board, KhetMove move) {
		khetBoard.makeMove(move);
		repaint();
	}

	@Override
	public void mousePressed(MouseEvent e) {
	}

	@Override
	public void mouseReleased(MouseEvent e) {
	}

	@Override
	public void mouseEntered(MouseEvent e) {
	}

	@Override
	public void mouseExited(MouseEvent e) {
	}

	private void getAllLegalMoves(int x, int y) {
		if (x >= 0 && x < KhetBoard.BOARD_WIDTH && y >= 0
				&& y < KhetBoard.BOARD_HEIGHT) {
			KhetPiece piece = khetBoard.board[x][y];
			if (piece != null && piece.getType() != Type.SPHINX) {
				KhetMove move = new KhetMove(piece.getPlayerNumber(), x, y, 0);
				possibleMoves = new HashMap<Position, Integer>();
				for (int i = 0; i < 9; i++) {
					move.setAction(i);
					if (khetBoard.isLeagal(move)) {
						possibleMoves.put(
								new Position(move.getX(), move.getY())
										.add(khetBoard.calculateDMove(i)), i);
					}

				}
			}

		}
	}

	private KhetScore getBoardScore(KhetBoard board) {
		KhetScore khetScore = new KhetScore();
		for (int i = 0; i < KhetBoard.BOARD_WIDTH; ++i) {
			for (int j = 0; j < KhetBoard.BOARD_HEIGHT; ++j) {
				KhetPiece piece = board.board[i][j];
				if (piece != null) {
					switch (piece.getType()) {
					case PYRAMID:
						if (piece.getPlayerNumber() == 1) {
							khetScore.pyramidSilver++;
						} else {
							khetScore.pyramidRed++;
						}
						break;
					case ANUBIS:
						if (piece.getPlayerNumber() == 1) {
							khetScore.anubisSilver++;
						} else {
							khetScore.anubisRed++;
						}
						break;
					case SPHINX:
						if (piece.getPlayerNumber() == 1) {
							khetScore.sphinxSilver++;
						} else {
							khetScore.sphinxRed++;
						}
						break;
					case SCARAB:
						if (piece.getPlayerNumber() == 1) {
							khetScore.scarabSilver++;
						} else {
							khetScore.scarabRed++;
						}
						break;

					case PHARAON:
						if (piece.getPlayerNumber() == 1) {
							khetScore.pharaohSilver++;
						} else {
							khetScore.pharaohRed++;
						}
						break;
					}
				}
			}
		}

		return khetScore;
	}

	class KhetScore {
		int anubisRed;
		int anubisSilver;
		int pharaohRed;
		int pharaohSilver;
		int pyramidRed;
		int pyramidSilver;
		int scarabRed;
		int scarabSilver;
		int sphinxRed;
		int sphinxSilver;

	}

	private void playAudio() {
		if(!soundOn) return;
		try {
			AudioInputStream audioIn = AudioSystem
					.getAudioInputStream(new ByteArrayInputStream(lightSaber));
			Clip clip = AudioSystem.getClip();
			clip.open(audioIn);
			clip.start();
		} catch (Exception e) {
			System.err.println("Error playing sound" + e);
		}
	}

	public boolean isSoundOn() {
		return soundOn;
	}

	public void setSoundOn(boolean soundOn) {
		this.soundOn = soundOn;
	}

}
